perm filename NCOMPL.RPG[UP,DOC]1 blob
sn#323806 filedate 1977-12-21 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00013 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00002 00002 The MACLISP compiler is called NCOMPLR (for Number COMPiLeR)
C00007 00003 A User's Guide to the
C00010 00004
C00013 00005
C00016 00006
C00019 00007
C00022 00008
C00025 00009
C00028 00010
C00031 00011
C00034 00012
C00037 00013
C00038 ENDMK
C⊗;
The MACLISP compiler is called NCOMPLR (for Number COMPiLeR)
and is noted for being the "best" LISP compiler in existence. In
particular there are facilities for declaring the types of objects so
that a fair amount of open-coding is possible; this is especially
nice for numerical computations (hence the "N" in NCOMPLR). For more
information on this either locate an old MACLISP manual or ask RPG or
WLS.
To compile a file do:
R NCOMPLR
<target>←FN.EXT<(SWITCHES)>
The <target> is optional and defaults accordding to the switches.
Briefly the switches are:
T Talk: verbose mode
A Assemble: take a .LAP file and assemble it
F Fasload: take a source file and compile & assemble it
K Kill: take a source file and compile & assemble it but
kill the LAP file
The default names are FN.LAP for a compiled file and FN.FAS for an
assembled file. NCOMPLR understands ALIASes.
One can inform the compiler about one's intentions by placing
DECLARATIONs in the file. DECLARATIONs are of the form:
(DECLARE <decl1><decl1>...<decln>)
The compiler actually evauates each of the <decli>'s so
that (DECLARE (EVAL (READ))) is meaningful.
Some important other declarations are:
(SPECIAL var1 var2 ...) Says that var1, var2, etc are special
(UNSPECIAL var1 var2 ...) Says that var1, var2, etc are local
(*EXPR fn1 fn2 ..) fn1, fn2 etc are EXPRs
(*LEXPR fn1 ...) fn1 etc are LEXPRs
(*FEXPR fn1 ...) fn1 etc are FEXPRs
(**ARRAY arr1 ...) arr1 etc are arrays
(FIXNUM v1 ...) v1 etc are fixnums
(FLONUM v1 ...) v1 etc are flonums
(FIXNUM (fn1 type1 ...) ...) fn1 etc returns a fixnum and its arguments are
of the type specified.
(FLONUM (fn1 type1 ...) ...) as above
(NOTYPE v1 (fn1 type1 ...) ...) v1, fn1, etc have no type
(FIXSW T) all arithmetic is FIXNUM except that denoted by +$ etc
(FLOSW T) all arithmetic is FLONUM except that denoted by + etc
(FIXSW NIL)(FLOSW NIL) shuts off above
(SETQ SPECIAL T) all variables are special
(SETQ NFUNVARS T) no functional values allowed
(MACROS T) causes macros to be defined at runtime as well as compile time
(MACROS NIL) shuts off above
(GENPREFIX foo) causes auxilliary functions generated by the compiler to be
named foon etc.
(ARRAY* (type arr1 n1 arr2 n2 ...)...) says that arr1, arr2 etc are of type type
and that arr1 is n1 dimensional etc.
(ARITH (type1 fn1 ...) ...) says to replace a general arithmetic function with
a one-type function. Thus (ARITH (FIXNUM PLUS))
will replace occurrences of PLUS with +.
(MAPEX T) open code MAP type functions
(NOARGS T) suppress number-of-args information (saves space)
(MESSIOC chars) does (IOC chars), used for suppressing error messages or
directing them to the LAP file
(MUZZLED T) suppresses closed-compilation messages
A User's Guide to the
Fast Arithmetic Feature of the Lisp Compiler
Eric Rosen
4/25/72
1- Introduction
The fast-arithmetic feature of NCOMPLR attempts to take
advantage of the new uniform representation of numbers in NLISP
by open-coding arithmetic expressions, i.e. by generating
machine instructions to do arithmetic, in lieu of generating
calls to LISP's arithmetic functions. It will also generate
compare or conditional jump instructions in lieu of calls to
MINUSP, ZEROP, PLUSP, GREATERP, LESSP, SIGNP, and for numeric
arguments, EQUAL. In order to facilitate this, the user MUST
make declarations to the compiler, so the compiler knows which
of the user's functions and variables have numerical values, and
whether these values are fixnum or flonum. The compiler
introduces a new data type which has no analogue in the
interpreter, i.e. number quantities. A number quantity is
merely a machine number. A LISP number is actually a pointer to
a word in garbage-collectable FIXNUM or FLONUM space which
contains a machine number; a number quantity is a machine
number itself, with no intervening pointer. The compiler will
automatically handle the intermediate results of open-coded
PAGE 2
arithmetic expressions as number quantities. Also, by suitable
use of declarations, the user may inform the compiler that
certain of his variables are to be treated as number quantities.
Number quantities are not stored in garbage-collectable space,
but on the FIXNUM or FLONUM PDLs.
Open compilation is, of course, only relevant to single-
word arithmetic. Those users who wish to utilitize infinite-
precision integer arithmetic (BIGNUMS) MUST close-compile their
code.
2- Number Variables
The following declarations enable the user to tell the
ccmpiler which of his variables are always going to have either
fixnum or flonum values, and whether these values are fixed or
floating point:
(DECLARE (FIXNUM XVAR1 XVAR2 ... XVARn)
(FLONUM LVAR1 LVAR2 ... LVARn))
In the case of local variables (i.e. variables which have not
been declared or made special) these declarations indicate that
the value of the variable is a number quantity. In the case of
special variables, these declarations indicate to the compiler
that the value of the variable is a LISP number of the given
type. (For technical reasons, no special variable can have a
number quantity as its value.)
It is important to realize that number variables, i.e.
PAGE 3
variables whose values are number quantities, do not exist in
the interpreter. Thus a number variable is not a LISP variable
in the usual sense, and there are restrictions on its use. A
NUMBER VARIABLE MAY NEVER HAVE ANY OTHER VALUE THAN A NUMBER.
Furthermore, a fixnum number variable may never have a flonum
value, and vice versa. If these restrictions are ignored,
errors will result. The compiler will try to detect such
errors, but most of them cannot be detected at compile time.
Thus the user must be very careful to pay heed to these
restrictions. FAILURE TO DO SO WILL MEAN THAT CODE WHICH MAY
RUN PERFECTLY WELL UNDER THE INTERPRETER WILL RUN ERRONEOUSLY
WHEN COMPILED. The user should be particularly careful with
PROG variables that are also number variables. The interpreter
initializes all PROG variables to NIL. However, since number
variables may not have the value NIL, the compiler initializes
number PROG variables to 0 or 0.0. Because of this
inconsistency with the interpreter, IT IS AN ERROR TO USE
NUMERIC PROG VARIABLES BEFORE INITIALIZING THEM. Again, the
compiler will try to detect these errors, but will not always be
able to.
It is, of course, permissible to SETQ or LAMBDA-bind
number variables to any expression whose value is a number, be
it a number quantity or a LISP number. It is also permissible
to SETQ or LAMBDA-bind LISP variables to number variables. In
all cases the compiler will cause the correct conversion to be
PAGE 4
done.
It should be realized that if a number quantity is ever
CONS'd into a list structure, it will have to first be converted
into a LISP number. This is relatively expensive in time,
because repeated number CONSing will cause more frequent garbage
collections. The compiler makes every effort to avoid number
CONSing, and therefore number CONSing is done only when
absolutely necessary. However if the user keeps any numerical
parameters for the purpose of CONSing them into lists, he may be
better off making them regular LISP variables. (Parameters
which are to be used in arithmetic computation are, of course,
best kept in number variables.)
The following declaration is available for negating the
effect of previous FIXNUM or FLONUM declarations.
(DECLARE (NOTYPE VAR1 VAR2 ... VARn))
3- Declaring functions
The following declarations are available for declaring
functions to the compiler:
(DECLARE (FIXNUM (XFN1 ARG1 ... ARGn) ... (XFNm ARG1
... ARGn))
(FLONUM (LFN1 ARG1 .. . ARGn) .. . (LFNm ARG1
... ARGn))
(NOTYPE (NFN1 ARG1 ... ARGn) ... (NFNm ARG1
... ARGn)))
PAGE 5
Of course, variable declarations may be intermingled with
function declarations as in:
(DECLARE (FIXNUM VAR1 VAR2 (FN FIXNUM NOTYPE)))
The arguments to these FIXNUM, FLONUM, and NOTYPE declarations
are lists. The CAR of each list is the name of the function
being declared. The CDR of each list is a list of the types of
the arguments of the functions, where each argument type is
either 'FIXNUM' (or, as an abbreviation, any fixnum number),
'FLONUM' (or any flonum number), or 'NOTYPE' (or 'NIL'). The
main declaration tells what kind of value the function returns.
Hence these declarations have the effect of both declaring what
kinds of arguments the function takes, and what kind of value it
returns.
For example, suppose function FOO returns a floating
point number. Further, suppose FOO has three arguments - a
fixed-point number, a random list, and a floating point number.
The appropriate declaration is:
(DECLARE (FLONUM (FOO FIXNUM NOTYPE FLONUM)))
Suppose BAR is just like FOO, except that its value is not
numeric. The appropriate declaration is:
(DECLARE (NOTYPE (BAR FIXNUM NOTYPE FLONUM)))
Arguments may be omitted on the right, in which case NOTYPE is
assumed. For instance, if FOOBAR has three arguments , the
following two declarations are completely equivalent:
PAGE 6
(DECLARE (FIXNUM (FOOBAR FLONUM NOTYPE NOTYPE)))
and
(DECLARE (FIXNUM (FOOBAR FLONUM)))
THE SAME DECLARATION MUST BE IN FORCE WHEN A FUNCTION IS
COMPILED AS WHEN A CALL TO THAT FUNCTION IS COMPILED. If this
rule is not followed, the functions may not interface properly,
causing random results and/or LISP errors. Compiled functons
should interface properly with uncompiled functions , however,
regardless of declaratons.
4- Scope of declarations
Declarations are either global, or local to the nearest-
enclosing PROG or LAMBDA in which they appear. If a declaration
occurs which is not within a function definition, it is global.
It maintains its effects until countermanded by some other
global declaration or temporarily overridden by a local
declaration. Local declarations should always appear as the
first statements of the PROG or LAMBDA in which they occur.
They affect only the PROG or LAMBDA in which they occur, and
they take precedence over any conflicting global or superior (in
position) local declarations.
When a function declaration is used to declare the
arguments of the function, the scope is just the function in
question.
Only FIXNUM, FLONUM, and NOTYPE declarations are legal
PAGE 7
as local declarations. All other declarations must be global,
i.e. they cannot appear within a function body. It is not legal
to locally undeclare a variable (i.e. by using the NOTYPE
declaration). The only use of local function declarations is to
declare bound variable functions. For example, where 'X' is a
bound variable the declaration
(DECLARE (FIXNUM (X FIXNUM)))
means that 'X' is bound to a function whose first (perhaps only)
argument is a fixnum and which returns a fixnum value. In order
for this declaration to work properly, all the functions to
which 'X' may be bound must have been so declared when they were
compiled.
5- Open-coding arithmetic expressions
The compiler is able to open-code the following
arithmetic functions: PLUS, TIMES, DIFFERENCE, *DIF, QUOTIENT,
*QUO, ADD1, SUB1, REMAINDER, MINUS, ABS, FIX, FLOAT, MINUSP,
PLUSP, SIGNP, BOOLE, ROT, LSH, GREATERP, LESSP, and if its
arguments are numeric, EQUAL.
(DECLARE (CLOSED T)) inhibits open-coding. (DECLARE
(CLOSED NIL)) enables it, and is the default option. If the
compiler cannot figure out whether the arguments to the above
functions are fixnums or flonums, it will be forced to generate
a call to the appropriate routine in the interpreter. The
compiler makes use of the user's declarations to determine the
PAGE 8
types of the arguments. When the compiler encounters an
expression with an argument whose type it can not determine, it
tries to open-compile as much of the expression as it can. If
an expression has to be even partially closed-compiled the
compiler will print a warning message. This message is expected
to be of aid to users who would like open-compiling but who
aren't getting it because they forgot to make a declaration.
However, users who want some expressions to be closed-compiled
may want to inhibit this message by saying (DECLARE (MUZZLED
T)).
The compiler also offers the user other ways to force
open-compilation. If the user says (DECLARE (FIXSW T)) , the
compiler will assume that all arguments to open-codable
functions (except, of course, EQUAL) are fixnums. Similarly,
the user can (DECLARE (FLOSW T)) if he is only using flonums.
The LISP functions + , - , * , / , | , 1+ , 1- , are just like
PLUS , DIFFERENCE, TIMES, QUOTIENT, REMAINDER, ADD1, and SUB1
except that the former may take only fixnum arguments.
Therefore the compiler is able to always open-code the former.
Similarly, the LISP functions +$, -$, *$, /$, 1+$, and 1-$
(these are real dollar signs, not alt. modes) may take only
flonum arguments, and are always open-coded. The predicates >,
<, and = are also always open-coded; they correspond to
GREATERP, LESSP, and EQUAL except that they always have two
numerical arguments of the same type. (In these last 3 cases,
PAGE 9
the compiler need not know which type the arguments are). The
third way to force open-coding is by means of the ARITH
declaration. The declaration (DECLARE (ARITH (FIXNUM ADD1 SUB1)
(FLONUM QUOTIENT))) effectively causes all SUB1's to be replaced
by 1-'s, ADD1's by 1+'s, and QUOTIENTS by /$'s. To revert back
to normal (DECLARE (ARITH (NOTYPE ADD1 SUB1 QUOTIENT))).
These techniques just described are NOT a substitute for
declaring variables and functions. They are useful in cases
where the compiler will not be able to determine the type of an
argument, for instance (PLUS (CAR X) (CADR X)). Obviously
the compiler has no way of knowing that (CAR X) is going to be
a fixnum. So in a case like this, there is a definite advantage
to using + instead of PLUS. And if all the user's arithmetic is
in the same mode, a FIXSW or FLOSW declaration may be good for
him. If a (FIXSW T) declaration is made, the only way to do any
flonum arithmetic at all is to use a function which assumes that
its arguments are flonums, e.g. +$.
6- Arrays
The user should always declare his arrays to the
compiler. The ARRAY* declaration is available for this purpose.
(Note that 'ARRAY*' is not '*ARRAY; the latter is a LISP
function). For instance,
(DECLARE (ARRAY* (FIXNUM FOO 1 BAR 2)
(FLONUM ARY 3)
PAGE 10
(NOTYPE FOOBAR 1 ARY2 2)))
This declares FOO to be a 1-dimensionsal array of fixnums, BAR a
2-dimensional array of fixnums, ARY a 3-dimensional array of
flonums, FOOBAR a 1-dimensional array of random S-expressions,
and ARY2 a 2-dimensional array of S-expressionr. Undeclared
arrays will be handled properly, but at great loss of
efficiency. It should be understood that arrays cannot
[currently] contain machine numbers - only LISP s-expressions,
including LISP numbers, are permitted. However, by the end of
calendar year 1973, there will be two new types of arrays
implemented in LISP, the FIXNUM array and the FLONUM array,
which will in fact contain machine numbers of the stated type.
Accessing the elements of such arrays will be open-coded by
NCOMPLR, and the resultant code should be speed-competitive with
FORTRAN array accessing [except that there are no plans for
implementing the hairier optimizations done by really good
FORTRAN compilers}.
7- Miscellany
The fast-arithmetic compiler is loaded by 'NCOMPLR}K',
but all other interactions with it are exactly the same as with
complr [command line parsing, compilations switches, etc.].
The code it produces runs only in LISPs with version number
greater than 400.
For general information on LISP, see Lisp Archiv.
PAGE 11
Any bugs in the NCOMPLR should be reported to Eric Rosen
(ECR) or Jonl White (JONL).